#include "CentralCache.h"
#include "PageCache.h"

CentralCache CentralCache::_inst;

// 将一批内存块给线程内存池
size_t CentralCache::FetchRangeObj(size_t size, void *start, void *end, size_t batchNum)
{
    assert(size <= MAX_BYTES);
    //获取一个非空的Span
    size_t index = UtilClass::Index(size);

    pthread_mutex_lock(&(_cc_freelists[index].GetMtx()));   //加桶锁
    Span* span = GetOneSpan(_cc_freelists[index], size);
    assert(span);
    assert(span->_freelist->Empty());

    //取出一批节点
    start = span->_freelist->PopRange(batchNum);
    //start、end赋值，计算有多少个节点
    size_t actualNum = 1;
    void* cur = start;
    while(NextObj(cur))
    {
        actualNum++;
        cur = NextObj(cur);
    }
    end = cur;

    span->_useCount += actualNum;
    pthread_mutex_unlock(&(_cc_freelists[index].GetMtx()));   //解桶锁
    return actualNum;
}

// 获取一个非空的Span
Span* CentralCache::GetOneSpan(SpanList& list, size_t size)
{
    assert(size <= MAX_BYTES);
    size_t index = UtilClass::Index(size);
    //寻找非空Span
    Span* it = list.Begin();
    while(it != list.End())
    {
        if(it->_freelist->Empty())
            return it;
        else 
            it = it->_next;
    }

    //如果没有非空Span就要去页缓存中申请了
    //计算申请多少页内存空间
    int k = UtilClass::FetchPageNum(size);
    //申请空间
    pthread_mutex_unlock(&(_cc_freelists[index].GetMtx()));          //解桶锁
    pthread_mutex_lock(&PageCache::GetInstance()->GetMtx());         //加页缓存锁
    Span* newSpan = PageCache::GetInstance()->FetchFromPageCache(k);
    pthread_mutex_unlock(&PageCache::GetInstance()->GetMtx());       //解页缓存锁

    //设置newSpan属性
    newSpan->_isUse = true;
    newSpan->objSize = size;
    newSpan->_freelist->GetAddress() = (void*)(newSpan->_pageID >> PAGE_SHIFT);
    //计算获取的大内存块字节数
    size_t bytes = newSpan->_n << PAGE_SHIFT;
    //对获取的Span空间进行切分处理
    newSpan->_freelist->CutSpan(size, bytes);

    //将Span挂载到SpanList上
    pthread_mutex_lock(&(_cc_freelists[index].GetMtx()));            //加桶锁
    _cc_freelists[index].PushFront(newSpan);

    return newSpan;
}

// 回收一批内存块
void CentralCache::ReleaseListToCentralCache(void *start, size_t size)
{
    assert(start);
    size_t index = UtilClass::Index(size);
    // 循环遍历链表，将每个内存块放到指定的Span中
    pthread_mutex_lock(&(_cc_freelists[index].GetMtx()));            //加桶锁
    while (start)
    {
        // 获取Span的地址
        Span *span = PageCache::GetInstance()->MapAddressToSpan(start);
        assert(span->_useCount != 0);

        // 将该节点放入span中
        void *obj = start;
        start = NextObj(obj);
        NextObj(obj) = nullptr;

        span->_freelist->PushFront(obj);
        span->_useCount--;

        // 如果该span的内存块全部回来了，就把该内存块还给页缓存
        if (span->_useCount == 0)
        {
            // 将span从链表中取出来
            _cc_freelists[index].Erase(span);

            // 将span给页缓存
            pthread_mutex_unlock(&(_cc_freelists[index].GetMtx()));      //解桶锁
            pthread_mutex_lock(&PageCache::GetInstance()->GetMtx());     //加页缓存锁
            PageCache::GetInstance()->ReleaseSpanToRageCache(span);
            pthread_mutex_unlock(&PageCache::GetInstance()->GetMtx());   //解页缓存锁
            pthread_mutex_lock(&(_cc_freelists[index].GetMtx()));        //加桶锁
        }
    }
}